package org.fing.edu.uy.esbadp.motor.utils;

import org.fing.edu.uy.esbadp.core.AdpFlowCache;
import org.fing.edu.uy.esbadp.core.AdpFlowInterface;
import org.fing.edu.uy.esbadp.core.AdpFlowRET;
import org.fing.edu.uy.esbadp.core.AdpFlowRUT;
import org.fing.edu.uy.esbadp.core.AdpFlowSYNC;
import org.fing.edu.uy.esbadp.core.entity.GenericTreeNode;
import org.fing.edu.uy.esbadp.validation.ValidateArgument;

public class MergeTreeUtils {
	
	/**
	 * @param src - Estrategia a la cual se le va a concatenar la estartegia de cambio de contrato siempre y cuand sea factible.
	 * @param dest - Estrategia de cambio de contrato, en caso que no exista se debera mandar null, en caso de no enviar una estrategia de cambio de contrato valida retorna src.
	 * @return Retorna un GenericTreeNode&#60;AdpFlowInterface&#62; con el merge de los dos arboles, primero src y luego dest.
	 */
	public static GenericTreeNode<AdpFlowInterface> mergeTree(GenericTreeNode<AdpFlowInterface> src, GenericTreeNode<AdpFlowInterface> dest){
		if(ValidateArgument.isNotNull(src)){
			if(isValidContractChange(dest)){
				int valid = isValidMergeWithContractChange(src);
				switch (valid) {
					case 1:
						mergeTreeRecursive(src, dest);
						break;
					case 0:
						return src;
					default:
						return dest;
				}
			}
			return src;
		}else{
			return dest;
		}
	}
	
	private static boolean isValidContractChange(GenericTreeNode<AdpFlowInterface> dest) {
		if(ValidateArgument.isNotNull(dest) && ValidateArgument.isNotNull(dest.getData())){
			if(dest.getData() instanceof AdpFlowRUT){
				return true;
			}
		}
		return false;
	}

	private static int isValidMergeWithContractChange(GenericTreeNode<AdpFlowInterface> src) {
		if(ValidateArgument.isNotNull(src.getData())){
			if((src.getData() instanceof AdpFlowCache) || (src.getData() instanceof AdpFlowRET)){
				return 1;
			}
			return 0;
		}
		return -1;
	}

	private static void mergeTreeRecursive(GenericTreeNode<AdpFlowInterface> src, GenericTreeNode<AdpFlowInterface> dest){
		if(src.hasChildren()){
			for(int index = 0; index < src.getChildren().size(); index++) {
				GenericTreeNode<AdpFlowInterface> child = src.getChildAt(index);
				if(child.getData() instanceof AdpFlowSYNC){
					//remplace SYNC for dest strategy
					src.addChildAt(index, dest);
					//remove SYNC node to add strategy dest
					src.removeChildAt(index + 1);
					//add link to child SYNC
					for(GenericTreeNode<AdpFlowInterface> childForchild : child.getChildren()){
						addLinkLeafToSyncChild(dest, childForchild);
					}
				}
				mergeTreeRecursive(child, dest);
			}
		}else{
			src.addChild(dest);
		}
	}

	private static void addLinkLeafToSyncChild(GenericTreeNode<AdpFlowInterface> dest, GenericTreeNode<AdpFlowInterface> childForchild) {
		if(dest.hasChildren()){
			for(GenericTreeNode<AdpFlowInterface> child : dest.getChildren()){
				addLinkLeafToSyncChild(child, childForchild);
			}
		}else{
			dest.addChild(childForchild);
		}
	}

}
